package com.djf.service;

import com.djf.entity.TaskInfo;
import com.djf.mapper.ProcessInstanceMapper;
import com.djf.mapper.TaskInfoMapper;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Map;

/**
 * Flowable流程服务类
 * 提供企业级的流程管理功能
 * 
 * @author djf
 * @date 2025/01/20
 */
@Slf4j
@Service
@Transactional
public class FlowableService {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private ManagementService managementService;

    @Autowired
    private IdentityService identityService;

    @Autowired
    private ProcessInstanceMapper processInstanceMapper;

    @Autowired
    private TaskInfoMapper taskInfoMapper;

    /**
     * 部署流程定义
     * 
     * @param processName 流程名称
     * @param bpmnFile BPMN文件输入流
     * @return 部署ID
     */
    public String deployProcess(String processName, InputStream bpmnFile) {
        try {
            Deployment deployment = repositoryService.createDeployment()
                    .name(processName)
                    .addInputStream(processName + ".bpmn20.xml", bpmnFile)
                    .deploy();
            
            log.info("流程部署成功，部署ID: {}, 流程名称: {}", deployment.getId(), processName);
            return deployment.getId();
        } catch (Exception e) {
            log.error("流程部署失败: {}", e.getMessage(), e);
            throw new RuntimeException("流程部署失败: " + e.getMessage());
        }
    }

    /**
     * 启动流程实例
     * 
     * @param processDefinitionKey 流程定义Key
     * @param businessKey 业务Key
     * @param variables 流程变量
     * @param startUserId 启动用户ID
     * @return 流程实例ID
     */
    public String startProcess(String processDefinitionKey, String businessKey, 
                              Map<String, Object> variables, String startUserId) {
        try {
            // 设置启动用户
            identityService.setAuthenticatedUserId(startUserId);
            
            // 启动流程实例
            ProcessInstance processInstance = runtimeService
                    .startProcessInstanceByKey(processDefinitionKey, businessKey, variables);
            
            log.info("流程启动成功，流程实例ID: {}, 业务Key: {}, 启动用户: {}", 
                    processInstance.getId(), businessKey, startUserId);
            
            return processInstance.getId();
        } catch (Exception e) {
            log.error("流程启动失败: {}", e.getMessage(), e);
            throw new RuntimeException("流程启动失败: " + e.getMessage());
        } finally {
            identityService.setAuthenticatedUserId(null);
        }
    }

    /**
     * 完成任务
     * 
     * @param taskId 任务ID
     * @param variables 任务变量
     * @param comment 审批意见
     * @param userId 处理用户ID
     */
    public void completeTask(String taskId, Map<String, Object> variables, 
                           String comment, String userId) {
        try {
            // 设置处理用户
            identityService.setAuthenticatedUserId(userId);
            
            // 添加审批意见
            if (comment != null && !comment.trim().isEmpty()) {
                taskService.addComment(taskId, null, comment);
            }
            
            // 更新任务信息
            TaskInfo taskInfo = taskInfoMapper.selectByTaskId(taskId);
            if (taskInfo != null) {
                taskInfo.setStatus("COMPLETED");
                taskInfo.setCompleteTime(LocalDateTime.now());
                taskInfo.setComment(comment);
                if (taskInfo.getCreateTime() != null) {
                    taskInfo.setDuration(System.currentTimeMillis() - 
                        taskInfo.getCreateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
                }
                taskInfoMapper.updateById(taskInfo);
            }
            
            // 完成任务
            taskService.complete(taskId, variables);
            
            log.info("任务完成成功，任务ID: {}, 处理用户: {}", taskId, userId);
        } catch (Exception e) {
            log.error("任务完成失败: {}", e.getMessage(), e);
            throw new RuntimeException("任务完成失败: " + e.getMessage());
        } finally {
            identityService.setAuthenticatedUserId(null);
        }
    }

    /**
     * 查询用户待办任务
     * 
     * @param userId 用户ID
     * @return 待办任务列表
     */
    public List<Task> getTodoTasks(String userId) {
        return taskService.createTaskQuery()
                .taskAssignee(userId)
                .orderByTaskCreateTime()
                .desc()
                .list();
    }

    /**
     * 查询用户候选任务
     * 
     * @param userId 用户ID
     * @return 候选任务列表
     */
    public List<Task> getCandidateTasks(String userId) {
        return taskService.createTaskQuery()
                .taskCandidateUser(userId)
                .orderByTaskCreateTime()
                .desc()
                .list();
    }

    /**
     * 认领任务
     * 
     * @param taskId 任务ID
     * @param userId 用户ID
     */
    public void claimTask(String taskId, String userId) {
        try {
            taskService.claim(taskId, userId);
            
            // 更新任务信息
            TaskInfo taskInfo = taskInfoMapper.selectByTaskId(taskId);
            if (taskInfo != null) {
                taskInfo.setAssignee(userId);
                taskInfo.setStatus("ASSIGNED");
                taskInfoMapper.updateById(taskInfo);
            }
            
            log.info("任务认领成功，任务ID: {}, 用户ID: {}", taskId, userId);
        } catch (Exception e) {
            log.error("任务认领失败: {}", e.getMessage(), e);
            throw new RuntimeException("任务认领失败: " + e.getMessage());
        }
    }

    /**
     * 委派任务
     * 
     * @param taskId 任务ID
     * @param userId 委派给的用户ID
     */
    public void delegateTask(String taskId, String userId) {
        try {
            taskService.delegateTask(taskId, userId);
            log.info("任务委派成功，任务ID: {}, 委派给用户: {}", taskId, userId);
        } catch (Exception e) {
            log.error("任务委派失败: {}", e.getMessage(), e);
            throw new RuntimeException("任务委派失败: " + e.getMessage());
        }
    }

    /**
     * 转办任务
     * 
     * @param taskId 任务ID
     * @param userId 转办给的用户ID
     */
    public void assignTask(String taskId, String userId) {
        try {
            taskService.setAssignee(taskId, userId);
            log.info("任务转办成功，任务ID: {}, 转办给用户: {}", taskId, userId);
        } catch (Exception e) {
            log.error("任务转办失败: {}", e.getMessage(), e);
            throw new RuntimeException("任务转办失败: " + e.getMessage());
        }
    }

    /**
     * 挂起流程实例
     * 
     * @param processInstanceId 流程实例ID
     */
    public void suspendProcessInstance(String processInstanceId) {
        try {
            runtimeService.suspendProcessInstanceById(processInstanceId);
            log.info("流程实例挂起成功，流程实例ID: {}", processInstanceId);
        } catch (Exception e) {
            log.error("流程实例挂起失败: {}", e.getMessage(), e);
            throw new RuntimeException("流程实例挂起失败: " + e.getMessage());
        }
    }

    /**
     * 激活流程实例
     * 
     * @param processInstanceId 流程实例ID
     */
    public void activateProcessInstance(String processInstanceId) {
        try {
            runtimeService.activateProcessInstanceById(processInstanceId);
            log.info("流程实例激活成功，流程实例ID: {}", processInstanceId);
        } catch (Exception e) {
            log.error("流程实例激活失败: {}", e.getMessage(), e);
            throw new RuntimeException("流程实例激活失败: " + e.getMessage());
        }
    }

    /**
     * 删除流程实例
     * 
     * @param processInstanceId 流程实例ID
     * @param deleteReason 删除原因
     */
    public void deleteProcessInstance(String processInstanceId, String deleteReason) {
        try {
            runtimeService.deleteProcessInstance(processInstanceId, deleteReason);
            log.info("流程实例删除成功，流程实例ID: {}, 删除原因: {}", processInstanceId, deleteReason);
        } catch (Exception e) {
            log.error("流程实例删除失败: {}", e.getMessage(), e);
            throw new RuntimeException("流程实例删除失败: " + e.getMessage());
        }
    }

    /**
     * 查询流程定义列表
     * 
     * @return 流程定义列表
     */
    public List<ProcessDefinition> getProcessDefinitions() {
        return repositoryService.createProcessDefinitionQuery()
                .latestVersion()
                .orderByProcessDefinitionName()
                .asc()
                .list();
    }

    /**
     * 查询历史流程实例
     * 
     * @param processInstanceId 流程实例ID
     * @return 历史流程实例
     */
    public HistoricProcessInstance getHistoricProcessInstance(String processInstanceId) {
        return historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
    }

    /**
     * 查询历史任务列表
     * 
     * @param processInstanceId 流程实例ID
     * @return 历史任务列表
     */
    public List<HistoricTaskInstance> getHistoricTasks(String processInstanceId) {
        return historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByTaskCreateTime()
                .asc()
                .list();
    }
}